// John Lake
// CSCE 441

#include <stdio.h>
#include <list>
#include <iostream>
#include <fstream>
#include "./Polygon.h"
#include <gl/glut.h>
#include <gl/gl.h>	//
#include <cmath>	// ?


/******************************************************************
	Notes:
	Image size is 400 by 400 by default.  You may adjust this if
		you want to.
	You can assume the window will NOT be resized.
	Call clearFramebuffer to clear the entire framebuffer.
	Call setFramebuffer to set a pixel.  This should be the only
		routine you use to set the color (other than clearing the
		entire framebuffer).  drawit() will cause the current
		framebuffer to be displayed.
	As is, your scan conversion should probably be called from
		within the display function.  There is a very short sample
		of code there now.
	You may add code to any of the subroutines here,  You probably
		want to leave the drawit, clearFramebuffer, and
		setFramebuffer commands alone, though.
  *****************************************************************/

#define ImageW 200
#define ImageH 200
#define ImageD 200	
// defining the depth of the image, so that the workspace is a giant cube; this is for normalization
//   purposes, but there may be a better way to do this

float framebuffer[ImageH][ImageW][3];
float zBuffer[ImageH][ImageW];	// [y][x]

Polygon currPolygon;			// keep track of the polygon currently being drawn
ShadingMode shadingmode;



void importPolygon(char* filename);
int coordTrans(float f, int max);

/**
 * A function to translate from [-1,1] coordinates to
 * [[0, ImageH], [0,ImageW], [0,ImageD]] coordinates
 */
int coordTrans(float f, int max)	// can change to be ImageH
{
	f += 1;		// f is in [0,2]
	f /= 2;		// f is in [0,1]
	f *= max;	// f is scaled to [0, max]
	if( (f - (int)f) < 0.5)	
	{	// round down
		return (int)f;
	}
	else
	{
		return ( (int)f + 1);
	}
}


/**
 *	Imports information about a polygon from a .obj file (in .obj format)
 *	  Assumes that the coordinates are in order (type, x, y, z) and that
 *	  nothing is specified to greater than 10 digits.
 */
void importPolygon(char* filename)
{
	printf("file is:\t%s\n",filename);

//	std::vector<Point> vertices;
	char type[10], cx[10], cy[10], cz[10], space[256];
	float fx,fy,fz;
	int x,y,z;

	std::ifstream objFile(filename, std::ifstream::in);
	while(objFile.good())	// not EOF
	{
		objFile.getline(type, 10, ' ');	// use ' ' as delimiter
	
		if(type[0] == '#')
		{
			printf("Comment Line\n");
			objFile.getline(space,256);	// get the rest of the commented line; there may be a more elegant way
			continue;
		}
		if(type[0] == 'v')
		{
			objFile.getline(cx,10,' ');	// use ' ' as delimiter
			objFile.getline(cy,10,' ');	// use ' ' as delimiter
			objFile.getline(cz,10);		// will use '\n' as delimiter

			fx = atof(cx);
			fy = atof(cy);
//			fy *= -1;	// for the window coordinates; possibly affects the math =(
			fz = atof(cz);

			// Translate to proper coordinates
			x = coordTrans(fx, ImageW);
			y = coordTrans(fy, ImageH);
			z = coordTrans(fz, ImageD);
			// The z coordinate doesn't need to change since the z coordinate will 
			//    eventually be removed; WRONG - it affects the calculations of normals

			// For testing
			printf("%s | %d | %d | %d\n", type,x,y,z);

			// Handle adding vertex
			currPolygon.addVertex(Point(x,y,z));

		}
		if(type[0] == 'f')	// face
		{
			objFile.getline(cx,10,' ');	// use ' ' as delimiter
			objFile.getline(cy,10,' ');	// use ' ' as delimiter
			objFile.getline(cz,10);		// will use '\n' as delimiter

			x = atoi(cx);	// the vertex numbers
			y = atoi(cy);
			z = atoi(cz);

			// For testing
			printf("%s | %d | %d | %d\n", type,x,y,z);

			// Handle face
			currPolygon.addFace(x,y,z);
		}
	}

	// The Polygon is built
	printf("Updating polygon...");
	currPolygon.updatePolygon();
	printf("Complete!\n");
}

// Draws the scene
void drawit(void)
{
   glDrawPixels(ImageW,ImageH,GL_RGB,GL_FLOAT,framebuffer);
   glFlush();
}

// Clears framebuffer to black
void clearFramebuffer()
{
	int i,j;

	for(i=0;i<ImageH;i++) {
		for (j=0;j<ImageW;j++) {
			framebuffer[i][j][0] = 0.0;	// R
			framebuffer[i][j][1] = 0.0;	// G
			framebuffer[i][j][2] = 0.0;	// B
		}
	}
}

void clearZBuffer()
{
	for(int i = 0; i < ImageH; i++)
		for(int j = 0; j < ImageW; j++)
			zBuffer[i][j] = ImageD;	// the maximum depth of the workspace cube
}

// Sets pixel x,y to the color RGB
// I've made a small change to this function to make the pixels match
// those returned by the glutMouseFunc exactly - Scott Schaefer 
void setFramebuffer(int x, int y, float z, float R, float G, float B)
{
	// changes the origin from the lower-left corner to the upper-left corner
//	y = ImageH - 1 - y;	// originally added by Scott Schaefer


	// Check whether to draw this pixel or not using a zBuffer
//	if( z < zBuffer[y][x] )	// in depth, more negative is closer to the viewer
//		zBuffer[y][x] = z;	// this point is closer to the viewer - update zBuffer and continue drawing
//	else
	if(z > zBuffer[y][x])
		return;	// the point considered is behind an already-drawn point; no reason to redraw

	zBuffer[y][x] = z;

	if (R<=1.0)
		if (R>=0.0)
			framebuffer[y][x][0]=R;
		else
			framebuffer[y][x][0]=0.0;
	else
		framebuffer[y][x][0]=1.0;
	if (G<=1.0)
		if (G>=0.0)
			framebuffer[y][x][1]=G;
		else
			framebuffer[y][x][1]=0.0;
	else
		framebuffer[y][x][1]=1.0;
	if (B<=1.0)
		if (B>=0.0)
			framebuffer[y][x][2]=B;
		else
			framebuffer[y][x][2]=0.0;
	else
		framebuffer[y][x][2]=1.0;
}



void display(void)
{
	clearFramebuffer();
	clearZBuffer();
	currPolygon.drawPolygon(shadingmode);
	drawit();
}


void keyboard(unsigned char key, int x, int y)
{
	
	switch(key) {
	case '1':
		// Flat Shading
		shadingmode = FLAT;
		break;

	case '2':
		// Gouraud shading
		shadingmode = GOURAUD;
		break;

	case '3':
		// Phong shading
		shadingmode = PHONG;
		break;
		
	default:
		break;
	}

	glutPostRedisplay();
}

void init(void)
{
	gluOrtho2D ( 0, ImageW - 1, ImageH - 1, 0 );
	clearFramebuffer();
	clearZBuffer();
}

int main(int argc, char** argv)
{
//	inProgress = NULL;
//	next_color.r = 0.5;
//	next_color.g = 0.5;
//	next_color.b = 0.5;
//	drawing_rectangle = false;
	currPolygon = Polygon(ImageW, ImageH);
	shadingmode = FLAT;

	printf("Workspace is still a cube..\n");

	glutInit(&argc,argv);
	glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
	glutInitWindowSize(ImageW,ImageH);
	glutInitWindowPosition(100,100);
	glutCreateWindow("John Lake - Homework 4");

	init();	

	//importPolygon(argv[1]);
	importPolygon("cube2.obj");

	glutDisplayFunc(display);
//	glutMouseFunc( mouseDraw );
	glutKeyboardFunc( keyboard );
	glutMainLoop();
	return 0;
}
